using System;
using System.Collections.Generic;

namespace nToolbox.Collections
{
	/// <summary>
	/// Represents a Dictionary that holds lists of <typeparamref name="TValue"/>
	/// keyed on <typeparamref name="TKey"/>.
	/// </summary>
	public interface IListMap<TKey, TValue>
	{
		/// <summary>
		/// Adds <paramref name="value"/> to the list for <paramref name="key"/>,
		/// creating a new list if needed.
		/// </summary>
		void Add(TKey key, TValue value);
		void Clear();
		/// <summary>
		/// Retutrns true if the map contains <paramref name="key"/>.
		/// </summary>
		bool Contains(TKey key);
		/// <summary>
		/// Gets the number of keys present.
		/// </summary>
		int Count { get; }
		/// <summary>
		/// Gets the list of values for key <paramref name="key"/>,
		/// or null if there are no values.
		/// </summary>
		IList<TValue> Get(TKey key);
		/// <summary>
		/// Removes the entire list of values for key <paramref name="key"/>.
		/// </summary>
		bool RemoveAll(TKey key);
		/// <summary>
		/// Removes the value <paramref name="value"/> from the list
		/// for key <paramref name="key"/>.
		/// </summary>
		bool Remove(TKey key, TValue value);
		/// <summary>
		/// Gets the list associated with key <paramref name="key"/>
		/// if there is one.
		/// </summary>
		/// <returns>True if the key is present, else false.</returns>
		bool TryGetValue(TKey key, out IList<TValue> list);
	}
	
	[Serializable]
	public class ListMap<TKey, TValue> : IListMap<TKey, TValue>
	{
		readonly Dictionary<TKey, IList<TValue>> innerMap;

		public ListMap()
		{
			innerMap = new Dictionary<TKey, IList<TValue>>();
		}

		public void Add(TKey key, TValue value)
		{
			IList<TValue> list;

			if (!innerMap.TryGetValue(key, out list))
			{
				list = new List<TValue>();
				innerMap.Add(key, list);
			}
			
			list.Add(value);
		}

		public void Clear()
		{
			innerMap.Clear();
		}

		public bool Contains(TKey key)
		{
			return innerMap.ContainsKey(key);
		}

		public int Count
		{
			get { return innerMap.Count; }
		}

		public IList<TValue> Get(TKey key)
		{
			IList<TValue> list;
			return innerMap.TryGetValue(key, out list) ? list : null;
		}

		public bool RemoveAll(TKey key)
		{
			return innerMap.Remove(key);
		}

		public bool Remove(TKey key, TValue value)
		{
			IList<TValue> list;
			if (innerMap.TryGetValue(key, out list)) 
				return list.Remove(value);
			else 
				return false;
		}

		public bool TryGetValue(TKey key, out IList<TValue> list)
		{
			return innerMap.TryGetValue(key, out list);
		}
	}
}
    